package com.carter.springframework.beans.factory.xml;

import cn.hutool.core.util.StrUtil;
import com.carter.springframework.beans.exception.BeanException;
import com.carter.springframework.beans.factory.config.BeanDefinition;
import com.carter.springframework.beans.factory.config.BeanReference;
import com.carter.springframework.beans.factory.config.PropertyValue;
import com.carter.springframework.beans.factory.support.AbstractBeanDefinitionReader;
import com.carter.springframework.beans.factory.support.BeanDefinitionRegistry;
import com.carter.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import com.carter.springframework.core.io.Resource;
import com.carter.springframework.core.io.ResourceLoader;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;
import org.xml.sax.SAXException;

import java.io.IOException;
import java.io.InputStream;

/**
 * xml方式读取bean定义
 */
public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
    public XmlBeanDefinitionReader(BeanDefinitionRegistry registry) {
        super(registry);
    }

    public XmlBeanDefinitionReader(BeanDefinitionRegistry registry, ResourceLoader resourceLoader) {
        super(registry, resourceLoader);
    }

    @Override
    public void loadBeanDefinitions(Resource resource) throws BeanException {
        try (InputStream inputStream = resource.getInputStream()) {
            doLoadBeanDefinitions(inputStream);
        } catch (IOException | ClassNotFoundException | DocumentException | SAXException e) {
            throw new BeanException("读取bean定义时出错：", e);
        }
    }

    /**
     * 真正通过xml读取bean定义的方法实现
     *
     * @param inputStream
     * @throws BeanException
     */
    private void doLoadBeanDefinitions(InputStream inputStream) throws BeanException, ClassNotFoundException, DocumentException, SAXException {
        SAXReader reader = new SAXReader();
        //安全措施，防止xxe攻击
        reader.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
        Document document = reader.read(inputStream);
        Element root = document.getRootElement();

        Element componentScan = root.element("component-scan");
        if (null != componentScan) {
            String scanPath = componentScan.attributeValue("base-package");
            if (StrUtil.isEmpty(scanPath)) {
                throw new BeanException("base package can not be null or empty");
            }
            scanPackage(scanPath);
        }

        for (Element bean : root.elements("bean")) {
            // 解析标签
            String id = bean.attributeValue("id");
            String name = bean.attributeValue("name");
            String className = bean.attributeValue("class");
            String initMethodName = bean.attributeValue("init-method");
            String destroyMethodName = bean.attributeValue("destroy-method");
            String scope = bean.attributeValue("scope");
            // 获取Class，方便获取类中的名称
            Class<?> clazz = Class.forName(className);
            // 优先级id>name
            String beanName = StrUtil.isNotEmpty(id) ? id : name;
            if (StrUtil.isEmpty(beanName)) {
                beanName = StrUtil.lowerFirst(clazz.getSimpleName());
            }
            // 定义Bean
            BeanDefinition beanDefinition = new BeanDefinition(clazz);
            beanDefinition.setInitMethodName(initMethodName);
            beanDefinition.setDestroyMethodName(destroyMethodName);
            // 读取属性并填充
            buildProperty(bean, beanDefinition);
            if (getRegistry().containsBeanDefinition(beanName)) {
                throw new BeanException("Duplicate beanName[" + beanName + "] is not allowed");
            }
            if (StrUtil.isNotBlank(scope)) {
                beanDefinition.setScope(scope);
            }
            // 注册BeanDefinition
            getRegistry().registerBeanDefinition(beanName, beanDefinition);
        }

    }

    /**
     * 填充beanDefinition属性
     * @param bean
     * @param beanDefinition
     */
    private void buildProperty(Element bean, BeanDefinition beanDefinition) {
        // 读取属性并填充
        for (Element property : bean.elements("property")) {
            // 解析标签：property
            String attrName = property.attributeValue("name");
            String attrValue = property.attributeValue("value");
            String attrRef = property.attributeValue("ref");
            // 获取属性值：引入对象、值对象
            Object value = StrUtil.isNotEmpty(attrRef) ? new BeanReference(attrRef) : attrValue;
            // 创建属性信息
            PropertyValue propertyValue = new PropertyValue(attrName, value);
            beanDefinition.getPropertyValues().addPropertyValue(propertyValue);
        }
    }


    @Override
    public void loadBeanDefinitions(String... location) throws BeanException {
        for (String s : location) {
            loadBeanDefinitions(s);
        }

    }

    @Override
    public void loadBeanDefinitions(String location) throws BeanException {
        ResourceLoader resourceLoader = getResourceLoader();
        Resource resource = resourceLoader.getResource(location);
        loadBeanDefinitions(resource);
    }

    private void scanPackage(String path){
        String[] basePackages = StrUtil.splitToArray(path, ",");
        ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(getRegistry());
        scanner.doScan(basePackages);
    }


}
